]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
st-widget: Sort actors by distance from the focused actor
authorJasper St. Pierre <jstpierre@mecheye.net>
Mon, 4 Nov 2013 22:12:22 +0000 (17:12 -0500)
committerJasper St. Pierre <jstpierre@mecheye.net>
Wed, 4 Dec 2013 16:22:55 +0000 (11:22 -0500)
Sorting actors by the distance in the axis of movement first and against
the axis otherwise means that if we have a situation like:

  A      F
   B

where "F" is the focused actor, and it slightly overlaps with B vertically,
then we'll choose "B" to go left, rather than "A", which is most likely
what the user intended.

This is especially apparent in the overview where slight window size
differences mean we might not get an exact grid shape.

https://bugzilla.gnome.org/show_bug.cgi?id=644306

src/st/st-widget.c

index d63af1eae31de5cc8d4f3c56cbc1e40fce008177..6aa3258d3c5d7e8a49ecbc448556b3c59f188296 100644 (file)
@@ -1861,83 +1861,45 @@ filter_by_position (GList            *children,
 }
 
 
-typedef struct {
-  GtkDirectionType direction;
-  ClutterActorBox box;
-} StWidgetChildSortData;
+static void
+get_midpoint (ClutterActorBox *box,
+              int             *x,
+              int             *y)
+{
+  *x = (box->x1 + box->x2) / 2;
+  *y = (box->y1 + box->y2) / 2;
+}
+
+static double
+get_distance (ClutterActor    *actor,
+              ClutterActorBox *bbox)
+{
+  int ax, ay, bx, by, dx, dy;
+  ClutterActorBox abox;
+  ClutterVertex abs_vertices[4];
+
+  clutter_actor_get_abs_allocation_vertices (actor, abs_vertices);
+  clutter_actor_box_from_vertices (&abox, abs_vertices);
+
+  get_midpoint (&abox, &ax, &ay);
+  get_midpoint (bbox, &bx, &by);
+  dx = ax - bx;
+  dy = ay - by;
+
+  /* Not the exact distance, but good enough to sort by. */
+  return dx*dx + dy*dy;
+}
 
 static int
-sort_by_position (gconstpointer  a,
+sort_by_distance (gconstpointer  a,
                   gconstpointer  b,
                   gpointer       user_data)
 {
   ClutterActor *actor_a = (ClutterActor *)a;
   ClutterActor *actor_b = (ClutterActor *)b;
-  StWidgetChildSortData *sort_data = user_data;
-  GtkDirectionType direction = sort_data->direction;
-  ClutterActorBox abox, bbox;
-  ClutterVertex abs_vertices[4];
-  int ax, ay, bx, by;
-  int cmp, fmid;
-
-  /* Determine the relationship, relative to motion in @direction, of
-   * the center points of the two actors. Eg, for %GTK_DIR_UP, we
-   * return a negative number if @actor_a's center is below @actor_b's
-   * center, and postive if vice versa, which will result in an
-   * overall list sorted bottom-to-top.
-   */
-
-  clutter_actor_get_abs_allocation_vertices (actor_a, abs_vertices);
-  clutter_actor_box_from_vertices (&abox, abs_vertices);
-  ax = (int)(abox.x1 + abox.x2) / 2;
-  ay = (int)(abox.y1 + abox.y2) / 2;
-  clutter_actor_get_abs_allocation_vertices (actor_b, abs_vertices);
-  clutter_actor_box_from_vertices (&bbox, abs_vertices);
-  bx = (int)(bbox.x1 + bbox.x2) / 2;
-  by = (int)(bbox.y1 + bbox.y2) / 2;
-
-  switch (direction)
-    {
-    case GTK_DIR_UP:
-      cmp = by - ay;
-      break;
-    case GTK_DIR_DOWN:
-      cmp = ay - by;
-      break;
-    case GTK_DIR_LEFT:
-      cmp = bx - ax;
-      break;
-    case GTK_DIR_RIGHT:
-      cmp = ax - bx;
-      break;
-    default:
-      g_return_val_if_reached (0);
-    }
-
-  if (cmp)
-    return cmp;
+  ClutterActorBox *box = user_data;
 
-  /* If two actors have the same center on the axis being sorted,
-   * prefer the one that is closer to the center of the current focus
-   * actor on the other axis. Eg, for %GTK_DIR_UP, prefer whichever
-   * of @actor_a and @actor_b has a horizontal center closest to the
-   * current focus actor's horizontal center.
-   *
-   * (This matches GTK's behavior.)
-   */
-  switch (direction)
-    {
-    case GTK_DIR_UP:
-    case GTK_DIR_DOWN:
-      fmid = (int)(sort_data->box.x1 + sort_data->box.x2) / 2;
-      return abs (ax - fmid) - abs (bx - fmid);
-    case GTK_DIR_LEFT:
-    case GTK_DIR_RIGHT:
-      fmid = (int)(sort_data->box.y1 + sort_data->box.y2) / 2;
-      return abs (ay - fmid) - abs (by - fmid);
-    default:
-      g_return_val_if_reached (0);
-    }
+  return get_distance (actor_a, box) - get_distance (actor_b, box);
 }
 
 static gboolean
@@ -2016,7 +1978,7 @@ st_widget_real_navigate_focus (StWidget         *widget,
     }
   else /* direction is an arrow key, not tab */
     {
-      StWidgetChildSortData sort_data;
+      ClutterActorBox sort_box;
       ClutterVertex abs_vertices[4];
 
       /* Compute the allocation box of the previous focused actor. If there
@@ -2032,36 +1994,35 @@ st_widget_real_navigate_focus (StWidget         *widget,
       if (from)
         {
           clutter_actor_get_abs_allocation_vertices (from, abs_vertices);
-          clutter_actor_box_from_vertices (&sort_data.box, abs_vertices);
+          clutter_actor_box_from_vertices (&sort_box, abs_vertices);
         }
       else
         {
           clutter_actor_get_abs_allocation_vertices (widget_actor, abs_vertices);
-          clutter_actor_box_from_vertices (&sort_data.box, abs_vertices);
+          clutter_actor_box_from_vertices (&sort_box, abs_vertices);
           switch (direction)
             {
             case GTK_DIR_UP:
-              sort_data.box.y1 = sort_data.box.y2;
+              sort_box.y1 = sort_box.y2;
               break;
             case GTK_DIR_DOWN:
-              sort_data.box.y2 = sort_data.box.y1;
+              sort_box.y2 = sort_box.y1;
               break;
             case GTK_DIR_LEFT:
-              sort_data.box.x1 = sort_data.box.x2;
+              sort_box.x1 = sort_box.x2;
               break;
             case GTK_DIR_RIGHT:
-              sort_data.box.x2 = sort_data.box.x1;
+              sort_box.x2 = sort_box.x1;
               break;
             default:
               g_warn_if_reached ();
             }
         }
-      sort_data.direction = direction;
 
       if (from)
-        children = filter_by_position (children, &sort_data.box, direction);
+        children = filter_by_position (children, &sort_box, direction);
       if (children)
-        children = g_list_sort_with_data (children, sort_by_position, &sort_data);
+        children = g_list_sort_with_data (children, sort_by_distance, &sort_box);
     }
 
   /* Now try each child in turn */